package
{
	import Dialogs.CharacterDialog;
	
	import com.greensock.TweenLite;
	import com.greensock.easing.Bounce;
	import com.greensock.easing.Expo;
	
	import flash.display.Sprite;
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	import flash.events.MouseEvent;
	import flash.events.TimerEvent;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.media.Sound;
	import flash.net.URLRequest;
	import flash.utils.Timer;
	
	public class Game implements IEventDispatcher, IPersistable
	{
		public static const PPS:int = 50; // Pixels per square
		public static const PADDING:int = 10;
		public static const WIDTH_COUNT:int = 5;
		public static var clientFilesPath:String = '';
		public static var instance:Game;
		public static var stage:Stage;
		public static var config:Config;
		public static var fbid:String;

		public var display:GameDisplay;
		public var hud:Hud;
		public var mall:Mall;
		
		private var m_persistanceLoader:PersistanceLoader;
		private var m_inputManager:GameInputManager;
		private var m_paused:Boolean = false;	
		private var m_gameModes:Vector.<IGameMode> = new Vector.<IGameMode>;
		private var m_eventDispatcher:EventDispatcher;
		
		// Things that need to be persisted
		private var m_saveCounter:int = 0;
		private var m_gameObjectIdCounter:int = 0;
		private var m_gameObjects:Vector.<GameObject> = new Vector.<GameObject>;
		public var player:Player = new Player();
		// -- Things that need to be persisted
		
		private var m_tutorialTimer:Timer = new Timer(10000);
		
		private var m_mallOverlay:Sprite = new Sprite;
		public var gameWorldOverlayLayer:Sprite = new Sprite;		
		
		public var tutorialStep:int = 0;
		
		public function get inputManager():GameInputManager{
			return m_inputManager;
		}
		
		public function Game(){	
			m_tutorialTimer.addEventListener(TimerEvent.TIMER, onTutorialTimerTick);
			m_eventDispatcher = new EventDispatcher(this);
			m_persistanceLoader = new PersistanceLoader(this);
			
			mall = new Mall();
			display = new GameDisplay();
			display.sync(mall.display);
			display.sync(m_mallOverlay);
			
			hud = new Hud(this);
			hud.addShopperButton.addEventListener(MouseEvent.CLICK, onAddShopper);
			hud.addStoreButton.addEventListener(MouseEvent.CLICK, onAddStore);
			hud.expandButton.addEventListener(MouseEvent.CLICK, onExpand);
			hud.doneButton.addEventListener(MouseEvent.CLICK, onHudDoneClick);
			
			startBossDialog();
			hud.doTutorialStep(tutorialStep);

			
			display.initDisplay();
			display.addChild(mall.display);

			gameWorldOverlayLayer.addChild(m_mallOverlay);
			m_mallOverlay.addChild(mall.overlay);			
			
			m_gameModes.push(new GMDefault());
			m_inputManager = new GameInputManager(this,m_gameModes);
			
			var s:Sound = new Sound(new URLRequest("assets/bg.mp3"));
			s.play(0,int.MAX_VALUE);
			
			SoundManager.init();
			
			m_persistanceLoader.addEventListener(Event.COMPLETE, onPersistanceLoadComplete);
			m_persistanceLoader.load();			
		}

		
		public function getNumberShoppers():int{
			return m_gameObjects.length;
		}

		
		public function loadStartingTestGame():void{
			player.cash = Game.config.startingCash;
			mall.allowableRectangle = new Rectangle(-Game.config.baseStoreWidth,0, Game.config.baseStoreWidth*2, 2);
			
			var i:int;
			
			var shopper:GameObject;
			shopper = new GameObject(mall, m_gameObjects);
			addShopper(shopper);
			shopper = new GameObject(mall, m_gameObjects);
			addShopper(shopper);
			shopper = new GameObject(mall, m_gameObjects);
			addShopper(shopper);			
			
			var esc:Escalator = new Escalator();
			mall.escalators.push(esc);
			esc.location = new Point(1,0);
			mall.display.addChild(esc.display);
			
			esc = new Escalator();
			esc.location = new Point(-2,0);
			mall.escalators.push(esc);
			mall.display.addChild(esc.display);
			
//			mall.buyStore('tools',new Point(0,0), player);
//			mall.buyStore('tools',new Point(0,1), player);
			
		}
		
		
		private function startBossDialog():void{
			var charDialog:CharacterDialog = new CharacterDialog('Crooked Joe', 'assets/boss1.png', "So Brian, you think you can make it as a mall owner?\n"
				+ "Well I don't think so buddy. I run these parts, so if you know what's good for ya, stay outta my territory kapisch?");
			hud.showAsMainDialog(charDialog.display);
			charDialog.onCloseCallback = function(me:MouseEvent):void{
				var charDialog:CharacterDialog = new CharacterDialog('Sally Your Advisor', 'assets/advisor.png', 
					"That Crooked Joe is such a sleaze bag. Lets stick to the business plan and soon enough we'll be stealing his customer " + 
					"and sending that jerk out of business!\n\n Let's get started!");

				hud.showAsMainDialog(charDialog.display);
				charDialog.onCloseCallback = function(me:MouseEvent):void{
					hud.addStoreButton.alpha = 0;
					hud.addStoreButton.x -= 200;
					hud.addStoreButton.visible = true;
					TweenLite.to(hud.addStoreButton, 1, {alpha: 1, x: hud.addStoreButton.x + 200, ease: Bounce.easeOut});
					hud.hideMainDialog();
					m_tutorialTimer.start();
				}
			}
			
		}		
		
		public function centerDisplay():void{
			display.y = Game.stage.stageHeight - 110;		
			display.x = Game.stage.stageWidth/2;
			gameWorldOverlayLayer.x = display.x;
			gameWorldOverlayLayer.y = display.y;
		}		

		private function onTutorialTimerTick(te:TimerEvent):void{
			var restepTime:int = 15000;
			var nextStepTime:int = 15000;
			m_tutorialTimer.stop();
			switch(tutorialStep){
				case 0:
					if(mall.stores.length <= 0){
						var charDialog:CharacterDialog = new CharacterDialog('Sally Your Advisor', 'assets/advisor.png',  "Looks like we already have potential customers! Let's build some shops for them.");
						hud.showAsMainDialog(charDialog.display);
						charDialog.onCloseCallback = function(me:MouseEvent):void{
							hud.hideMainDialog();
							m_tutorialTimer.start();
						}
						m_tutorialTimer.delay = restepTime;
					} else {
						m_tutorialTimer.delay = nextStepTime;
						tutorialStep = 1;
						m_tutorialTimer.start();
					}
					break;
				case 1:
					if(getNumberShoppers() <= 3){
						charDialog = new CharacterDialog('Sally Your Advisor', 'assets/advisor.png',  "We're off to a great start! Let's advertise and see if we can STEAL some customers from Crooked Joe!");
						hud.showAsMainDialog(charDialog.display);
						charDialog.onCloseCallback = function(me:MouseEvent):void{
							hud.hideMainDialog();
							hud.doTutorialStep(tutorialStep);
							m_tutorialTimer.start();
						}
						m_tutorialTimer.delay = restepTime;
					} else {
						m_tutorialTimer.delay = nextStepTime;
						tutorialStep = 2;
						m_tutorialTimer.start();
					}					
					break;
				case 2:
					if(getNumberShoppers() >= 3){
						charDialog = new CharacterDialog('Crooked Joe', 'assets/boss1.png', "Brian you SCUMBAG! I know you're stealing my customers! I WILL CRUSH YOU!");
						hud.showAsMainDialog(charDialog.display);
						charDialog.onCloseCallback = function(me:MouseEvent):void{
							charDialog = new CharacterDialog('Sally Your Advisor', 'assets/advisor.png',  "That'll show him! If we keep this up, at the end of the year we'll definitely beat him for the City's Best 1-star mall award!");
							hud.showAsMainDialog(charDialog.display);
							charDialog.onCloseCallback = function(me:MouseEvent):void{
								charDialog = new CharacterDialog('Sally Your Advisor', 'assets/advisor.png',  "Keep getting as many of his customers as you can, but be careful your stores don't go out of stock!");
								hud.showAsMainDialog(charDialog.display);
								charDialog.onCloseCallback = function(me:MouseEvent):void{
									hud.hideMainDialog();
									hud.doTutorialStep(tutorialStep);
									m_tutorialTimer.start();
									tutorialStep = 3;
								}								
							}
						}
					}
					break;
				case 3:
					charDialog = new CharacterDialog('Sally Your Advisor', 'assets/advisor.png',  "As customers shop, your stores will level up! Click on ones that level up to increase the quality of their goods or their inventory.");
					hud.showAsMainDialog(charDialog.display);
					charDialog.onCloseCallback = function(me:MouseEvent):void{
						hud.hideMainDialog();
						tutorialStep = 4;
						m_tutorialTimer.delay = restepTime*2;
						m_tutorialTimer.start();
					}
					break;
				case 4:
					if(mall.expansionsPurchased == 0){
						charDialog = new CharacterDialog('Sally Your Advisor', 'assets/advisor.png',  "Oh boy! The cash is REALLY flowing now! Our customers are getting cramped we ought to expand!!");
						hud.showAsMainDialog(charDialog.display);
						m_tutorialTimer.stop();
						charDialog.onCloseCallback = function(me:MouseEvent):void{
							m_tutorialTimer.start();
							hud.hideMainDialog();
							hud.doTutorialStep(tutorialStep);
						}					
					} else {
						tutorialStep = 5;
						m_tutorialTimer.start();
					}
					break;
			}
		}
		
		public function onPersistanceLoadComplete(e:Event):void{
			dispatchEvent(new Event(Event.INIT));						
		}

		public function findSelectedObject(mousePosition:Point):ISelectable{
			var store:Store = mall.pickStore(mousePosition);
			return store;
		}
		
		public function onClick(selectable:ISelectable):void{
			if(selectable is Store){
				(selectable as Store).hideLevelUp();
				mall.toggleMenu(selectable as Store);	
			}
			
		}

		public function pause():void{
			m_paused  = true;
		}
		
		public function resume():void{
			m_paused = false;
		}
		
		public function buyStore(storeName:String, location:Point):void{
			mall.buyStore(storeName, location, player);
			leaveGameMode();	
		}		
		
		public function moveStore(store:Store, location:Point):void{
			mall.moveStore(store, location);
			leaveGameMode();
		}
		
		private function initializeObjectIds(player:Player, savedObject:Object):void{
			var i:int, l:int;
			for(i = 0, l = savedObject['gameObjects'].length; i < l; i++){
				var gameObject:GameObject = new GameObject(mall, m_gameObjects);
				gameObject.id = savedObject['gameObjects'][i]['id'];
				m_gameObjects.push(gameObject);			
			}	
			
			mall.initializeObjectIds(savedObject['mall']);
		}
		
		public function cancelClick():void{
			resetGameModes();
		}
	
	
		public function getGameObjectById(goId:int):GameObject{
			for(var i:int = 0, l:int = m_gameObjects.length; i < l; i++){
				if(m_gameObjects[i]['id'] == goId){
					return m_gameObjects[i];
				}
			}
			
			throw new Error("game object id" + goId + " not found");
		}
		
		private function initializeGameObjects(gameObjects:Array):void{
			for(var i:int = 0, l:int = gameObjects.length; i < l; i++){
				var gameObject:GameObject = getGameObjectById(gameObjects[i]['id']);
				gameObject.loadSavedObject(gameObjects[i]);
				mall.enterMall(gameObject);
			}
		}
		
		public function canAffordStore(store:Store):Boolean{
			return (player.cash >= store.value);
		}
		
		private function onHudDoneClick(me:MouseEvent):void{
			leaveGameMode();
		}
		
		private function onAddShopper(me:MouseEvent):void{
			
			hud.confirmationDialog("Spend $" + String(getCostForNextShopper()) + " on Ads for another shopper?", onAddShopperConfirm);
		}
		
		private function onExpand(me:MouseEvent):void{
			hud.confirmationDialog("Spend $" + String(getCostForNextExpansion()) + " to expand your mall?", onExpandConfirm);
		}
		
		public function getCostForNextShopper():int{
			var v1:Number = Math.pow(m_gameObjects.length, 2);
			var v2:Number = Math.pow(v1, 1.75);
			return Math.floor(v2);
		}
		
		public function getCostForNextExpansion():int{
			return	mall.getCostForNextExpansion();
			
		}		
		
		private function onExpandConfirm(confirm:Boolean):void{
			if(confirm){
				
				var cost:int = mall.getCostForNextExpansion();
				if(player.cash < cost){
					return;
				}
				
				player.cash -= cost;
				mall.expand();
				m_persistanceLoader.save();				
			}
		}		
		
		private function onAddShopperConfirm(confirm:Boolean):void{
			if(confirm){
				
				var cost:int = getCostForNextShopper();
				if(player.cash < cost){
					return;
				}
				
				
				
				SoundManager.play('assets/cheer.mp3');
				player.cash -= cost;
				
				var gameObject:GameObject;
				if(Math.random() < .5){
					gameObject = new GameObject(mall, m_gameObjects,Util.getRandomSNUser());
				} else {
					gameObject = new GameObject(mall, m_gameObjects);
				}
				addShopper(gameObject);
				
				hud.updateNumberShoppers();
				m_persistanceLoader.save();				
			}
		}
		
		private function addShopper(gameObject:GameObject):void{
			gameObject.forceMallEntry();
			gameObject.id = m_gameObjectIdCounter++;
			m_gameObjects.push(gameObject);			
		}
		
		
		public function buyPlayer(snUser:SNUser):void{
			var gameObject:GameObject = new GameObject(mall, m_gameObjects, snUser);
			mall.enterMall(gameObject);
			m_gameObjects.push(gameObject);
			m_persistanceLoader.save();
		}
		
		private function onAddStore(me:MouseEvent):void{
			hud.showBuyShopsBar();
		}
		
		public function buyStoreMode(storeName:String):void{
			enterGameMode(new GMStorePlacement(storeName, GMStorePlacement.MODE_BUY));
		}		
		
		public function step(delta:int):void{
			var i:int, length:int;
			
			if(m_paused){ return; }
			
			display.step(delta);
			
			mall.step(delta);
			
			for(i = 0, length = m_gameObjects.length; i < length; i++){
				m_gameObjects[i].step(delta);
			}
		}
		
		public function command(command:String):void{

		}
		
		public function leaveGameMode():void{
			hud.hideDoneButton();
			var gameMode:IGameMode = m_gameModes.shift();
			gameMode.cleanup();
		}
		

		private function resetGameModes():void{
			while(m_gameModes.length != 1){
				m_gameModes[0].cleanup();
				m_gameModes.shift();
			}
		}

		public function enterGameMode(gameMode:IGameMode):void{
			resetGameModes();
			m_gameModes.unshift(gameMode);
		}
		
		public function onMouseDrag(dragVector:Point):void{
			display.onMouseDrag(dragVector);
			hud.hideTooltip();
		}		
		
		
		
		/*********************************************
		 * IPersistable
		 *****************************************/
		public function getSaveObject():Object{
			var saveObj:Object = new Object();
			saveObj['saveCounter'] = m_saveCounter++;
			saveObj['gameObjectCounter'] = m_gameObjectIdCounter;
			saveObj['mall'] = mall.getSaveObject();
			saveObj['gameObjects'] = [];
			
			for each(var go:GameObject in m_gameObjects){
				saveObj['gameObjects'].push(go.getSaveObject());
			}
			
			saveObj['player'] = player.getSaveObject();
			return saveObj;
		}
		
		public function loadSavedObject(savedObject:Object):void{
			m_gameObjectIdCounter = savedObject['gameObjectCounter'];
			player.loadSavedObject(savedObject['player']);
			initializeObjectIds(player, savedObject);
			
			mall = new Mall();
			mall.loadSavedObject(savedObject['mall']);

			initializeGameObjects(savedObject['gameObjects']);
			m_saveCounter = savedObject['saveCounter'];
			
		}
		/*********************************************
		 * -- IPersistable
		 *****************************************/
		
		
		
		/*********************************************
		 * IEventDispatcher
		 *****************************************/
		
		
		public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void{
			m_eventDispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference);
		}
		
		public function dispatchEvent(event:Event):Boolean{
			return m_eventDispatcher.dispatchEvent(event);
		}
		
		public function hasEventListener(type:String):Boolean{
			return m_eventDispatcher.hasEventListener(type);	
		}
		
		public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void{
			m_eventDispatcher.removeEventListener(type, listener, useCapture);
		}
		
		public function willTrigger(type:String):Boolean{
			return m_eventDispatcher.willTrigger(type);
		}
		
		/*********************************************
		 * -- IEventDispatcher
		 *****************************************/		
	}
}